package com.example.demo.elasticsearch.service.impl;

import com.alibaba.fastjson.JSON;
import com.example.demo.elasticsearch.domain.Domain;
import com.example.demo.elasticsearch.domain.DomainTemplate;
import com.example.demo.elasticsearch.service.ISearchService;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryAction;
import org.elasticsearch.index.reindex.DeleteByQueryRequestBuilder;
import org.elasticsearch.rest.RestStatus;
import org.modelmapper.ModelMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: htc
 * @date: Created in 16:56 2018/12/29.
 */
//@Service
public class SearchServiceImpl implements ISearchService {

    private final static Logger log = LoggerFactory.getLogger(SearchServiceImpl.class);

    /**
     * 索引名
     */
    private static final String INDEX_NAME = "domain";

    /**
     * 索引类型
     */
    private static final String INDEX_TYPE = "domain";

    @Autowired
    private TransportClient client;

    @Resource
    private ModelMapper modelMapper;

    @Override
    public void index(Long id) {
        //从DB中查出数据
        Domain domain = getDomainById(id);
        if (domain == null) {
            log.error("index domain {} does not exist !", id);
            return;
        }

        //PO转换成对应的es模型
        DomainTemplate template = new DomainTemplate();
        modelMapper.map(domain, template);


        //查询
        SearchRequestBuilder requestBuilder = this.client.prepareSearch(INDEX_NAME).setTypes(INDEX_TYPE)
                .setQuery(QueryBuilders.termQuery("id", id));
        log.debug(requestBuilder.toString());
        SearchResponse searchResponse = requestBuilder.get();
        long totalHit = searchResponse.getHits().getTotalHits();
        boolean success;
        if (totalHit == 0) {
            //创建
            success = create(template);
        } else if (totalHit == 1) {
            //更新
            String esId = searchResponse.getHits().getAt(0).getId();
            success = update(esId, template);
        } else {
            //删除重建
            success = deleteAndCreate(totalHit, template);
        }
        if (success) {
            log.debug("Index success with domain " + id);
        }
    }

    @Override
    public void remove(Long id) {

    }

    /**
     * 创建文档
     *
     * @param template
     * @return
     */
    private boolean create(DomainTemplate template) {
        IndexResponse response = this.client.prepareIndex(INDEX_NAME, INDEX_TYPE)
                .setSource(JSON.toJSONString(template), XContentType.JSON)
                .get();
        log.debug("Create index with domian: " + template.getId());
        if (response.status() == RestStatus.CREATED) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 更新文档
     *
     * @param esId
     * @param template
     * @return
     */
    private boolean update(String esId, DomainTemplate template) {
        UpdateResponse response = this.client.prepareUpdate(INDEX_NAME, INDEX_TYPE, esId)
                .setDoc(JSON.toJSONString(template), XContentType.JSON)
                .get();
        log.debug("Update index with domain: " + template.getId());
        if (response.status() == RestStatus.OK) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 删除并创建
     *
     * @param template
     * @return
     */
    private boolean deleteAndCreate(long totalHit, DomainTemplate template) {
        DeleteByQueryRequestBuilder builder = DeleteByQueryAction.INSTANCE
                .newRequestBuilder(this.client)
                .filter(QueryBuilders.termQuery("id", template.getId()))
                .source(INDEX_NAME);
        log.debug("Delete by query for domain: " + builder);
        BulkByScrollResponse response = builder.get();
        long deleted = response.getDeleted();
        if (deleted != totalHit) {
            log.warn("Need delete {}, but {} deleted!", totalHit, deleted);
            return false;
        } else {
            return create(template);
        }
    }

    /**
     * 模拟DB查询
     *
     * @param id
     * @return
     */
    private Domain getDomainById(Long id) {
        List<Domain> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(new Domain((long) i));
        }

        for (Domain domain : list) {
            if (id.equals(domain.getId())) {
                return domain;
            }
        }
        return null;
    }
}
